package cn.com.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Logger;

import cn.com.logger.LoggerManager;

public class NIOManager implements Runnable {
	private Logger logger = LoggerManager.getLogger(NIOManager.class);
	private final Set<SelectionKey> selectionKeys = new HashSet<SelectionKey>();
	private Queue<NIOServerHolder> pendingServers = new LinkedBlockingQueue<NIOServerHolder>();
	private Selector selector;
	private final static long SELECTION_TIMEOUT = 1000;

	private void initListening(NIOServerHolder info) {
		InetSocketAddress isAddress = new InetSocketAddress(info.getPort());
		try {
			selector = Selector.open();
			ServerSocketChannel serverSocket = ServerSocketChannel.open();
			serverSocket.socket().bind(isAddress);
			serverSocket.configureBlocking(false);

			SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT, info);
			selectionKeys.add(selectionKey);
			logger.debug("Listening ... on " + info.getPort());
		} catch (IOException e) {
			logger.error("Can't init listening", e);
		}
	}

	@Override
	public void run() {
		try {
			while (true) {
				registerPendingServers();
				if(selector.select(SELECTION_TIMEOUT) > 0){
					processSelectKeys();
				}
			}
		} catch (Exception e) {
		}
		
	}
	
	private void processSelectKeys() {
		Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
		while(iterator.hasNext()){
			SelectionKey key = iterator.next();
			processKey(key);
			iterator.remove();
		}
	}

	private void processKey(SelectionKey key) {
		Holder holder = (Holder) key.attachment();
	}

	public void registerServer(int port, NIOServerListener factory){
		NIOServerHolder info = new NIOServerHolder(port, factory);
		pendingServers.add(info);
		selector.wakeup();
	}
	
	private int registerPendingServers(){
		int max = pendingServers.size();
		int count = 0;
		NIOServerHolder info;
		while (max-- > 0 && (info = pendingServers.poll()) != null) {
			initListening(info);
			count++;
		}
		return count;
	}
}
